#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
文件名称: query_api.py
文档保存目录：/e:/DEV_VISION/backend/api/query_api.py
完整存储路径: backend/api/query_api.py
功能说明:
    学生数据查询业务API，支持固定查询、组合查询、分页、排序和导出功能。
    本模块专注于业务逻辑，配置接口已迁移至 config_api.py。
更新日志:
    v1.3 - 架构清理，移除配置接口，专注业务逻辑
    v1.2 - 配置驱动重构，支持动态字段映射和智能表关联
"""

from flask import Blueprint, request, jsonify, make_response
from sqlalchemy import and_, or_, func, text
from sqlalchemy.orm import aliased, joinedload
import json
import io
import pandas as pd
from datetime import datetime

from backend.infrastructure.database import db
from backend.models.student import Student, StudentExtension
from backend.models.vision import VisionRecord
from backend.models.intervention import InterventionRecord
from backend.config.config_manager import ConfigManager

query_api = Blueprint("query_api", __name__)
config_manager = ConfigManager()

# ==================== 辅助函数 ====================

def get_table_alias(table_name):
    """
    获取表对应的SQLAlchemy模型类（配置驱动）
    
    📋 功能说明：
    - 通过配置管理器获取数据库表名对应的SQLAlchemy模型类
    - 支持配置驱动的表映射，避免硬编码
    
    🔗 调用位置：
    - build_field_condition_new() 中用于获取字段所属的模型类
    
    Args:
        table_name (str): 数据库表名（如 'students', 'student_extensions' 等）
        
    Returns:
        SQLAlchemy模型类: 对应的模型类（Student, StudentExtension 等）
        
    Example:
        get_table_alias('students') → Student
        get_table_alias('student_extensions') → StudentExtension
    """
    return config_manager.get_table_class(table_name)

def format_field_value_new(value, field_config):
    """
    🔧 字段值格式化器：根据字段配置对原始值进行格式化
    
    📋 核心作用：
    - 将数据库原始值转换为前端显示格式
    - 根据字段类型（数值、日期、布尔等）应用相应格式化规则
    - 处理None值和异常情况
    
    🔗 调用位置：
    - format_query_results_v2() 中用于格式化每个字段值
    
    Args:
        value: 原始字段值（可能是None、数值、日期等）
        field_config: 字段配置字典（包含type、precision等）
        
    Returns:
        str: 格式化后的字符串值
        
    Example:
        format_field_value_new(3.14159, {'type': 'number_range', 'precision': 2}) → '3.14'
        format_field_value_new(True, {'type': 'checkbox'}) → '是'
    """
    if value is None:
        return ""
    
    field_type = field_config.get('type', 'text') if field_config else 'text'
    
    if field_type == 'number_range':
        precision = field_config.get('precision', 2) if field_config else 2
        if isinstance(value, (int, float)):
            return round(value, precision)
    elif field_type == 'date':
        if hasattr(value, 'strftime'):
            return value.strftime('%Y-%m-%d')
    elif field_type == 'time':
        if hasattr(value, 'strftime'):
            return value.strftime('%H:%M')
    elif field_type == 'checkbox':
        return "是" if value else "否"
    
    return str(value)

# ==================== 新版配置驱动的核心函数 ====================

def get_field_config_new(field_name):
    """
    获取字段配置信息（新版本）
    
    📋 功能说明：
    - 通过配置管理器获取指定字段的完整配置信息
    - 专门用于查询模块的字段配置获取
    
    🔗 调用位置：
    - 主要在查询相关函数中使用，获取字段的类型、显示名称等配置
    
    Args:
        field_name (str): 字段名称
        
    Returns:
        dict: 字段配置字典，包含 type、display_name、table 等信息
        
    Example:
        get_field_config_new('name') → {'type': 'text', 'display_name': '姓名', 'table': 'students'}
    """
    return config_manager.get_query_field_config(field_name)  # 使用查询专用函数

def build_query_conditions_new(query_params):
    """
    🔍 查询模块核心：构建跨表查询条件
    
    📋 核心作用：
    - 解析前端查询参数，生成SQLAlchemy查询条件
    - 识别跨表字段，确定需要JOIN的表
    - 配置驱动：通过config_manager.get_query_field_config()获取字段信息
    
    🔗 与get_query_field_config的关系：
    1. 本函数遍历查询参数（如grade=高一、age=15）
    2. 对每个参数调用config_manager.get_query_field_config(param_name)
    3. get_query_field_config返回字段配置，包含关键的'table'信息
    4. 根据'table'值决定是否需要JOIN student_extensions等表
    5. 构建正确的查询条件
    
    ⚠️ 问题解决：
    - 修复了跨表字段（grade、age、data_year）查询失败问题
    - 通过get_query_field_config获取准确的表信息，避免默认查students表
    
    Args:
        query_params: 前端查询参数字典 {'grade': '高一', 'age': 15, ...}
        
    Returns:
        tuple: (查询条件列表, 需要JOIN的表集合)
        
    Example:
        build_query_conditions_new({'grade': '高一'}) → 
        ([StudentExtension.grade == '高一'], {'students', 'student_extensions'})
    """
    conditions = []
    query_tables = set(['students'])  # 基础表
    
    for param_name, param_value in query_params.items():
        if not param_value or param_name in ['page', 'per_page', 'sort_field', 'sort_order', 'columns']:
            continue
            
        # 特殊处理：有随访记录的学生
        if param_name == 'has_followup_records' and param_value in ['true', '1', 'yes']:
            from backend.models.followup import FollowupRecord
            from sqlalchemy import exists
            conditions.append(exists().where(FollowupRecord.student_id == Student.id))
            continue
            
        # 使用查询专用配置获取字段信息
        field_config = config_manager.get_query_field_config(param_name)  # 使用查询专用函数
        if not field_config:
            continue
            
        # 记录需要关联的表
        table_name = field_config.get('table', 'students')
        query_tables.add(table_name)
        
        # 根据字段类型构建条件
        condition = build_field_condition_new(param_name, param_value, field_config)
        if condition is not None:
            conditions.append(condition)
    
    return conditions, query_tables

def build_field_condition_new(field_name, field_value, field_config):
    """
    🔍 查询条件构建器：将单个字段查询转换为SQLAlchemy条件
    
    📋 核心作用：
    - 接收字段配置（来自get_query_field_config），生成具体的SQL查询条件
    - 根据字段类型（text/int/select等）选择合适的查询操作符
    - 处理跨表字段的表名映射（通过field_config['table']）
    
    🔗 调用链位置：
    build_query_conditions_new() → build_field_condition_new() → SQLAlchemy条件
    
    ⚠️ 关键处理：
    - 使用field_config['table']获取正确的表类（避免默认students表错误）
    - 根据字段类型选择ilike/==/>=/<=等操作符
    - 验证字段在目标表中是否存在
    
    Args:
        field_name: 字段名（如'grade'）
        field_value: 查询值（如'高一'）
        field_config: 字段配置字典（含'table'和'type'等信息）
        
    Returns:
        SQLAlchemy查询条件对象或None
        
    Example:
        build_field_condition_new('grade', '高一', {'table': 'student_extensions', 'type': 'text'})
        → StudentExtension.grade.ilike('%高一%')
    """
    field_type = field_config.get('type', 'text')
    table_name = field_config.get('table', 'students')
    table_class = get_table_alias(table_name)
    
    # 使用新版配置的字段类型处理
    if not table_class or not hasattr(table_class, field_name):
        print(f"警告：字段{field_name}不存在于表{table_name}")
        return None
        
    field_attr = getattr(table_class, field_name)
    
    try:
        if field_type == 'text':
            return field_attr.ilike(f'%{field_value}%')
        elif field_type == 'multi_select':
            if isinstance(field_value, list):
                return field_attr.in_(field_value)
            else:
                return field_attr == field_value
        elif field_type == 'dropdown':
            return field_attr == field_value
        elif field_type == 'number_range':
            return build_number_range_condition(field_attr, field_value, field_config)
        elif field_type == 'date':
            return build_date_condition(field_attr, field_value)
        elif field_type == 'checkbox':
            return field_attr == (field_value in ['true', '1', True])
        else:
            return field_attr == field_value
    except Exception as e:
        print(f"构建字段条件失败 {field_name}: {e}")
        return None

def build_number_range_condition(field_attr, field_value, field_config):
    """
    构建数值范围查询条件
    
    📋 功能说明：
    - 处理数值型字段的范围查询（如年龄 15-18 岁）
    - 支持最小值、最大值或精确值查询
    
    🔗 调用位置：
    - build_field_condition_new() 中当字段类型为 'number_range' 时调用
    
    Args:
        field_attr: SQLAlchemy字段属性对象
        field_value (dict|str|int): 查询值，可以是：
            - dict: {'min': 15, 'max': 18} 范围查询
            - str/int: 精确值查询
        field_config (dict): 字段配置信息
        
    Returns:
        SQLAlchemy查询条件: 数值范围条件或None
        
    Example:
        build_number_range_condition(Student.age, {'min': 15, 'max': 18}, {})
        → and_(Student.age >= 15, Student.age <= 18)
    """
    if isinstance(field_value, dict):
        conditions = []
        if 'min' in field_value and field_value['min'] is not None:
            conditions.append(field_attr >= float(field_value['min']))
        if 'max' in field_value and field_value['max'] is not None:
            conditions.append(field_attr <= float(field_value['max']))
        return and_(*conditions) if conditions else None
    else:
        try:
            return field_attr == float(field_value)
        except (ValueError, TypeError):
            return None

def build_date_condition(field_attr, field_value):
    """
    构建日期查询条件
    
    📋 功能说明：
    - 处理日期型字段的查询条件
    - 支持日期范围查询（开始日期到结束日期）和精确日期查询
    
    🔗 调用位置：
    - build_field_condition_new() 中当字段类型为 'date' 时调用
    
    Args:
        field_attr: SQLAlchemy日期字段属性对象
        field_value (dict|str): 查询值，可以是：
            - dict: {'start': '2023-01-01', 'end': '2023-12-31'} 日期范围
            - str: '2023-06-15' 精确日期
        
    Returns:
        SQLAlchemy查询条件: 日期条件或None
        
    Example:
        build_date_condition(VisionRecord.measure_date, {'start': '2023-01-01', 'end': '2023-12-31'})
        → and_(VisionRecord.measure_date >= '2023-01-01', VisionRecord.measure_date <= '2023-12-31')
    """
    if isinstance(field_value, dict):
        conditions = []
        if 'start' in field_value and field_value['start']:
            conditions.append(field_attr >= field_value['start'])
        if 'end' in field_value and field_value['end']:
            conditions.append(field_attr <= field_value['end'])
        return and_(*conditions) if conditions else None
    else:
        return field_attr == field_value

def build_smart_query(conditions, query_tables):
    """
    智能构建数据库查询（基础版本）
    
    📋 功能说明：
    - 根据查询条件和涉及的表，自动构建多表关联查询
    - 使用 OUTER JOIN 确保主表数据完整性
    - 通过 distinct() 防止多表关联产生重复记录
    
    🔗 调用位置：
    - 早期版本的查询构建，目前主要使用 build_smart_query_v2
    
    ⚠️ 注意事项：
    - 仅用于兼容性，新代码应使用 build_smart_query_v2
    - 功能相对简单，不支持复杂的字段映射
    
    Args:
        conditions (list): SQLAlchemy查询条件列表
        query_tables (set): 需要关联的表名集合
        
    Returns:
        SQLAlchemy Query对象: 配置好的查询对象
        
    Example:
        build_smart_query([Student.name.like('%张%')], {'students', 'student_extensions'})
        → Student查询，关联StudentExtension表
    """
    base_query = db.session.query(Student)
    # 直接进行表关联
    if 'student_extensions' in query_tables:
        base_query = base_query.outerjoin(StudentExtension, Student.id == StudentExtension.student_id)
    # 其它表关联可按需补充
    # 只查主表唯一ID，防止重复
    base_query = base_query.distinct(Student.id)
    # 直接应用条件过滤
    if conditions:
        base_query = base_query.filter(and_(*conditions))
    return base_query

def build_smart_query_v2(selected_columns, conditions):
    """
    智能构建多表查询（推荐版本）
    
    📋 功能说明：
    - 根据选中的字段自动分析需要关联的表
    - 使用预加载（joinedload）机制，一次查询获取所有关联数据
    - 支持虚拟字段处理，自动跳过不存在于数据库中的字段
    - 使用 OUTER JOIN 保证数据完整性，distinct() 防止重复
    
    🔗 调用位置：
    - query_students_new() 查询接口
    - export_students() 导出功能
    - 与 format_query_results_v2() 配合使用，实现高性能查询
    
    ✅ 优势特性：
    - 自动表关联：根据字段配置自动判断需要JOIN的表
    - 预加载优化：使用joinedload避免N+1查询问题
    - 虚拟字段支持：遇到虚拟字段直接跳过，不抛异常
    - 性能优异：一次查询获取所有需要的数据
    
    Args:
        selected_columns (list): 前端选择的字段列表
        conditions (list): 查询条件列表
        
    Returns:
        tuple: (SQLAlchemy Query对象, 字段表映射字典)
        
    Example:
        query, field_table_map = build_smart_query_v2(
            ['name', 'grade', 'measure_date'],
            [Student.name.like('%张%')]
        )
        # 返回预加载了student_extensions和vision_records的查询对象
    """
    from backend.models.student import Student
    from backend.models.student_extension import StudentExtension
    from backend.models.vision import VisionRecord
    from backend.models.intervention import InterventionRecord
    
    # 字段归属表
    table_map = {
        'students': Student,
        'student_extensions': StudentExtension,
        'vision_records': VisionRecord,
        'intervention_records': InterventionRecord
    }
    # 统计所有需要的表
    config = ConfigManager()
    field_table_map = {}
    for field in selected_columns:
        table = config.get_query_field_table(field)
        if not table:
            raise Exception(f"字段 {field} 未注册表归属")
        if table == 'virtual':
            # 虚拟字段不参与数据库查询，仅用于结果组装
            continue
        field_table_map[field] = table
    # 主表始终是Student
    query = db.session.query(Student)
    # 动态outerjoin
    if 'student_extensions' in field_table_map.values():
        query = query.outerjoin(StudentExtension, Student.id == StudentExtension.student_id)
    if 'vision_records' in field_table_map.values():
        query = query.outerjoin(VisionRecord, Student.id == VisionRecord.student_id)
    if 'intervention_records' in field_table_map.values():
        query = query.outerjoin(InterventionRecord, Student.id == InterventionRecord.student_id)
    # 预加载
    query = query.options(joinedload(Student.extensions))
    # 去重
    query = query.distinct(Student.id)
    # 条件
    if conditions:
        query = query.filter(and_(*conditions))
    return query, field_table_map

def format_query_results_v2(results, selected_columns, field_table_map):
    """
    【当前主力函数】直接从多表对象提取字段，高性能版本
    
    ✅ 推荐使用：这是当前的主要字段值获取函数
    
    优势：
    - 使用预加载的关联对象（joinedload），无需额外数据库查询
    - 支持批量处理多条记录，性能优异
    - 完整的错误处理和日志记录
    - 支持虚拟字段处理
    
    工作原理：
    1. 通过 build_smart_query_v2 预加载所有需要的关联表数据
    2. 直接从内存中的对象获取字段值，无需数据库查询
    3. 统一的字段格式化和错误处理
    
    与旧函数的区别：
    - get_field_value_from_result_new: 每个字段都要查询数据库，性能差
    - format_query_results_v2: 使用预加载数据，性能好
    
    参数：
    - results: 预加载关联数据的Student对象列表
    - selected_columns: 需要提取的字段列表
    - field_table_map: 字段到表的映射关系
    
    返回：格式化后的结果列表
    """
    import logging
    config = ConfigManager()
    virtual_fields = set(config.get_virtual_field_registry().keys())
    formatted_results = []
    for obj in results:
        row = {}
        for field in selected_columns:
            if field in virtual_fields:
                row[field] = ""
                continue
            try:
                table = field_table_map[field]
                # 特殊处理
                if field == 'age':
                    ext = obj.extensions[-1] if obj.extensions else None
                    row[field] = ext.age if ext and ext.age is not None else config.calculate_age(obj.birthday) if obj.birthday else ''
                    continue
                if table == 'students':
                    row[field] = getattr(obj, field, '') or ''
                elif table == 'student_extensions':
                    ext = obj.extensions[-1] if obj.extensions else None
                    row[field] = getattr(ext, field, '') if ext else ''
                elif table == 'vision_records':
                    vision = obj.vision_records[-1] if obj.vision_records else None
                    row[field] = getattr(vision, field, '') if vision else ''
                elif table == 'intervention_records':
                    intervention = obj.intervention_records[-1] if obj.intervention_records else None
                    row[field] = getattr(intervention, field, '') if intervention else ''
                else:
                    row[field] = ''
                # 格式化
                field_cfg = config.get_query_field_config(field)
                row[field] = format_field_value_new(row[field], field_cfg)
            except Exception as e:
                # 记录详细异常日志，包含字段名、表归属、异常类型和堆栈
                logging.error(f"[format_query_results_v2] 字段处理异常: field={field}, table={field_table_map.get(field)}, error={e}", exc_info=True)
                row[field] = f"[ERROR: {e}]"
        # vision_check_time特殊合成
        if 'measure_date' in row and 'measure_time' in row:
            try:
                row['vision_check_time'] = config.format_vision_check_time(row.get('measure_date'), row.get('measure_time'))
            except Exception as e:
                logging.error(f"[format_query_results_v2] vision_check_time合成异常: {e}", exc_info=True)
                row['vision_check_time'] = f"[ERROR: {e}]"
        formatted_results.append(row)
    return formatted_results

def format_query_results_new(results, selected_columns=None):
    """
    【已废弃函数 - 保留用于兼容性】配置驱动的结果格式化（旧版本）
    
    ⚠️ 注意：此函数已被 format_query_results_v2 取代，仅保留用于debug和兼容性
    
    问题：
    - 使用 get_field_value_from_result_new 进行字段值获取
    - 每个字段都需要额外的数据库查询，性能极差
    - 在大数据量时会导致 N+1 查询问题
    
    替代方案：
    - 新代码应使用 format_query_results_v2
    - 结合 build_smart_query_v2 使用预加载机制
    
    参数：
    - results: Student对象列表
    - selected_columns: 选中的字段列表（可选）
    
    返回：格式化后的结果列表
    """
    formatted_results = []
    all_fields = config_manager.get_all_query_fields()
    # 禁止兜底：selected_columns 为空时不做任何补全
    for result in results:
        row_data = {}
        for field_name in all_fields:
            if selected_columns and field_name not in selected_columns:
                continue
            field_config = config_manager.get_query_field_config(field_name)
            table_name = config_manager.get_query_field_table(field_name)
            field_value = get_field_value_from_result_new(result, field_name, table_name)
            formatted_value = format_field_value_new(field_value, field_config)
            row_data[field_name] = formatted_value
        if 'measure_date' in row_data and 'measure_time' in row_data:
            row_data['vision_check_time'] = config_manager.format_vision_check_time(
                row_data.get('measure_date'),
                row_data.get('measure_time')
            )
        formatted_results.append(row_data)
    return formatted_results

def get_field_value_from_result_new(result, field_name, table_name):
    """
    【已废弃函数 - 保留用于兼容性】从查询结果中获取字段值
    
    ⚠️ 注意：此函数已被 format_query_results_v2 取代，仅保留用于debug和兼容性
    
    用途：
    - 基于单个Student对象和表名映射获取字段值
    - 需要额外的数据库查询来获取关联表数据
    - 性能较差，因为每个字段都要单独查询数据库
    
    替代方案：
    - 新代码应使用 format_query_results_v2 函数
    - format_query_results_v2 使用预加载的关联对象，性能更好
    
    参数：
    - result: Student对象
    - field_name: 字段名
    - table_name: 字段所属表名
    
    返回：字段值（字符串）
    """
    try:
        # 特殊处理保持不变
        if field_name == 'age':
            # 先尝试从扩展表获取，如果没有则基于生日计算
            extension = db.session.query(StudentExtension)\
                .filter_by(student_id=result.id)\
                .order_by(StudentExtension.data_year.desc()).first()
            if extension and extension.age:
                return extension.age
            elif hasattr(result, 'birthday') and result.birthday:
                return config_manager.calculate_age(result.birthday)
            return ""
        
        if field_name == 'vision_check_time':
            vision = db.session.query(VisionRecord)\
                .filter_by(student_id=result.id)\
                .order_by(VisionRecord.measure_date.desc()).first()
            if vision:
                return config_manager.format_vision_check_time(vision.measure_date, vision.measure_time)
            return ""
        
        # 使用新版配置获取表映射
        if table_name == 'students':
            return getattr(result, field_name, "") or ""  # 修正：处理None值
        elif table_name == 'student_extensions':
            extension = db.session.query(StudentExtension)\
                .filter_by(student_id=result.id)\
                .order_by(StudentExtension.data_year.desc()).first()
            if extension:
                value = getattr(extension, field_name, "")
                return value if value is not None else ""  # 修正：处理None值
            return ""
        elif table_name == 'vision_records':
            vision = db.session.query(VisionRecord)\
                .filter_by(student_id=result.id)\
                .order_by(VisionRecord.measure_date.desc()).first()
            if vision:
                value = getattr(vision, field_name, "")
                return value if value is not None else ""  # 修正：处理None值
            return ""
        elif table_name == 'intervention_records':
            intervention_fields = config_manager.get_config("INTERVENTION_SUBFIELDS")
            if field_name in intervention_fields:
                has_intervention = db.session.query(InterventionRecord)\
                    .filter_by(student_id=result.id)\
                    .filter(getattr(InterventionRecord, field_name) == True).first()
                return "是" if has_intervention else "否"
        
        return ""
    except Exception as e:
        print(f"获取字段值失败 {field_name}: {e}")
        return ""



# ==================== 更新的API端点 ====================

@query_api.route('/api/students/query', methods=['GET'])
def query_students_new():
    """
    学生数据查询主接口（全配置驱动架构）
    
    📋 功能说明：
    - 提供灵活的学生数据查询功能，支持多字段组合查询
    - 采用全局唯一配置驱动架构，所有查询逻辑基于配置文件
    - 支持分页、排序、字段选择等高级查询功能
    - 自动处理多表关联，确保数据完整性和查询性能
    
    🔧 技术特性：
    - 配置驱动：字段映射、表关联完全基于配置，无硬编码
    - 多表智能关联：根据查询字段自动关联相关表
    - 预加载优化：使用joinedload避免N+1查询问题
    - 虚拟字段支持：支持计算字段和组合字段
    
    📊 查询参数：
    - columns: 逗号分隔的字段列表，如 'name,grade,measure_date'
    - page: 页码，默认1
    - per_page: 每页条数，默认20，最大100
    - sort_field: 排序字段
    - sort_order: 排序方向 (asc/desc)
    - 其他字段作为查询条件，如 grade=高一&age=15
    
    🔗 调用流程：
    1. 参数验证和解析
    2. build_query_conditions_new() 构建查询条件
    3. build_smart_query_v2() 构建多表查询
    4. format_query_results_v2() 格式化结果
    
    Returns:
        JSON响应: 包含查询结果、分页信息和元数据
        
    Example:
        GET /api/students/query?columns=name,grade&grade=高一&page=1&per_page=20
        返回高一年级学生的姓名和年级信息，第1页，每页20条
    """
    try:
        query_params = request.args.to_dict()
        print(f"收到查询参数: {query_params}")
        # 禁止主表查询传递 student_id
        if 'student_id' in query_params:
            return jsonify({'success': False, 'error': '主表查询禁止使用 student_id，请使用 id 或 education_id'}), 400
        # 验证查询字段
        if 'columns' in query_params:
            requested_columns = query_params['columns'].split(',')
            validation_result = config_manager.validate_query_fields(requested_columns)
            if not validation_result['is_valid']:
                print(f"无效字段: {validation_result['invalid_fields']}")
        else:
            requested_columns = config_manager.get_all_query_fields()[:20]
        # 条件
        conditions, _ = build_query_conditions_new(query_params)
        # 构建多表查询
        base_query, field_table_map = build_smart_query_v2(requested_columns, conditions)
        # 分页
        page = int(query_params.get('page', 1))
        per_page = min(int(query_params.get('per_page', 20)), 100)
        paginated = base_query.paginate(page=page, per_page=per_page, error_out=False)  # type: ignore
        print(f"查询到 {len(paginated.items)} 条记录，总计 {paginated.total} 条")
        # 格式化
        formatted_results = format_query_results_v2(paginated.items, requested_columns, field_table_map)
        return jsonify({
            'success': True,
            'students': formatted_results,
            'total': paginated.total,
            'page': page,
            'per_page': per_page,
            'total_pages': paginated.pages
        })
    except Exception as e:
        print(f"查询失败: {str(e)}")
        import traceback
        traceback.print_exc()
        return jsonify({
            'success': False,
            'error': f'查询失败: {str(e)}'
        }), 500

@query_api.route('/api/students/get', methods=['GET'])
def get_single_student():
    """
    获取单个学生详细信息接口
    
    📋 功能说明：
    - 根据学生ID获取完整的学生信息，包括基本信息和扩展信息
    - 支持多种ID参数格式（id 或 student_id）
    - 返回最新的扩展信息（按 data_year 降序）
    
    🔧 技术特性：
    - 参数兼容：同时支持 'id' 和 'student_id' 参数
    - 数据完整：包含学生基本表和扩展表的所有关键字段
    - 最新数据：自动获取最新年度的扩展信息
    
    📊 查询参数：
    - id (int): 学生ID（推荐使用）
    - student_id (int): 学生ID（兼容性参数）
    
    🔗 数据来源：
    - students 表：基本信息（姓名、学号、学校、联系方式等）
    - student_extensions 表：扩展信息（年级、年龄、身高体重等）
    
    Returns:
        JSON响应: 包含完整学生信息的响应对象
        
    Example:
        GET /api/students/get?id=123
        返回ID为123的学生的所有详细信息
    """
    try:
        # 兼容多种参数：id, student_id（数字），education_id（字符串）
        student_id = request.args.get('id', type=int) or request.args.get('student_id', type=int)
        education_id = request.args.get('education_id', type=str)
        
        if not student_id and not education_id:
            return jsonify({'success': False, 'message': '缺少学生ID参数（需要 id、student_id 或 education_id）'}), 400
        
        # 查询学生基本信息
        if student_id:
            # 使用数字ID查询
            student = Student.query.get(student_id)
        else:
            # 使用education_id查询
            student = Student.query.filter_by(education_id=education_id).first()
            
        if not student:
            return jsonify({'success': False, 'message': '未找到该学生'}), 404
        # 获取学生扩展信息（使用实际的student.id）
        extension = db.session.query(StudentExtension)\
            .filter_by(student_id=student.id)\
            .order_by(StudentExtension.data_year.desc()).first()
        # 构建返回数据 - 修复字段访问错误，确保包含id_card等完整信息
        student_data = {
            'id': student.id,
            'name': student.name,
            'education_id': student.education_id,
            'school': student.school,
            'gender': student.gender,
            'birthday': student.birthday.strftime('%Y-%m-%d') if student.birthday else '',
            'grade': extension.grade if extension else '',  # 从extension获取grade
            'class_name': student.class_name,  # 从student获取class_name
            'parent_name': student.parent_name,
            'parent_phone': student.parent_phone,
            'contact_address': student.contact_address,
            'id_card': student.id_card,  # 确保包含身份证号码
            'phone': student.phone,
            'region': student.region,
            'data_year': student.data_year if hasattr(student, 'data_year') else ''
        }
        # 添加扩展信息
        if extension:
            student_data['student_info'] = {
                'age': extension.age,
                'height': extension.height,
                'weight': extension.weight
            }
        return jsonify({
            'success': True,
            'student': student_data
        })
    except Exception as e:
        print(f"❌ get_single_student API错误: {str(e)}")
        return jsonify({'success': False, 'message': str(e)}), 500

# ==================== 保留原有的兼容性API端点 ====================

@query_api.route('/api/students/query', methods=['GET'])
def query_students():
    """
    学生查询接口（兼容性包装）
    
    📋 功能说明：
    - 为保持向后兼容性而保留的接口包装
    - 实际调用 query_students_new() 实现
    
    🔗 调用关系：
    - query_students() → query_students_new()
    
    ⚠️ 使用建议：
    - 新代码建议直接使用 query_students_new()
    - 此接口主要用于兼容旧版本前端调用
    
    Returns:
        JSON响应: 与 query_students_new() 相同的响应格式
    """
    return query_students_new()

# ==================== 导出功能API ====================

@query_api.route('/api/students/export', methods=['GET'])
def export_students():
    """
    学生数据导出接口（Excel格式）
    
    📋 功能说明：
    - 根据查询条件导出学生数据为Excel文件
    - 支持自定义字段选择和查询条件过滤
    - 使用与查询接口相同的配置驱动架构
    - 自动生成带时间戳的文件名
    
    🔧 技术特性：
    - 配置驱动：字段映射和显示名称完全基于配置
    - 高性能查询：使用 build_smart_query_v2 和预加载机制
    - 灵活导出：支持选择性字段导出和条件过滤
    - Excel优化：使用 xlsxwriter 引擎，支持大数据量导出
    
    📊 查询参数：
    - columns: 逗号分隔的字段列表，如 'name,grade,measure_date'
    - 其他字段作为过滤条件，如 grade=高一&age=15
    - 不支持分页参数，导出所有匹配的记录
    
    🔗 处理流程：
    1. 解析查询参数和字段选择
    2. build_query_conditions_new() 构建查询条件
    3. build_smart_query_v2() 构建多表查询
    4. format_query_results_v2() 格式化数据
    5. 生成Excel文件并返回
    
    📁 输出格式：
    - 文件格式: .xlsx (Excel 2007+)
    - 工作表名: '查询结果'
    - 表头: 使用配置中的 display_name
    - 文件名: student_data_YYYYMMDD_HHMMSS.xlsx
    
    Returns:
        Excel文件: 包含查询结果的Excel文件下载响应
        
    Example:
        GET /api/students/export?columns=name,grade&grade=高一
        导出所有高一学生的姓名和年级信息到Excel文件
    """
    try:
        # 获取查询参数
        query_params = request.args.to_dict()
        print(f"导出查询参数: {query_params}")
        # 获取选择的列
        selected_columns = query_params.get('columns', '').split(',') if query_params.get('columns') else None
        if not selected_columns:
            selected_columns = config_manager.get_all_query_fields()[:20]
        # 新版条件构建
        conditions, _ = build_query_conditions_new(query_params)
        # 构建新版多表查询
        base_query, field_table_map = build_smart_query_v2(selected_columns, conditions)
        results = base_query.all()
        print(f"导出数据条数: {len(results)}")
        # 格式化数据
        formatted_results = format_query_results_v2(results, selected_columns, field_table_map)
        # 创建DataFrame
        if formatted_results:
            display_columns = {str(col): str(config_manager.get_query_field_display_name(col)) for col in selected_columns}
            df = pd.DataFrame(formatted_results)
            df = df[selected_columns].rename(columns=display_columns)  # type: ignore
        else:
            df = pd.DataFrame()
        # 生成Excel文件
        output = io.BytesIO()
        with pd.ExcelWriter(output, engine='xlsxwriter') as writer:  # type: ignore
            df.to_excel(writer, sheet_name='查询结果', index=False)
        output.seek(0)
        # 返回文件
        response = make_response(output.getvalue())
        response.headers['Content-Type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        response.headers['Content-Disposition'] = f'attachment; filename=student_data_{datetime.now().strftime("%Y%m%d_%H%M%S")}.xlsx'
        return response
    except Exception as e:
        print(f"导出失败: {str(e)}")
        import traceback
        traceback.print_exc()
        return jsonify({
            'success': False,
            'error': f'导出失败: {str(e)}'
        }), 500


